package com.star.core.global;

import com.star.common.enums.BizStatus;
import com.star.common.exception.BizException;
import com.star.common.vo.Result;
import com.star.security.exception.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 统一异常处理
 *
 * @author yangzj
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 自定义业务异常
     *
     * @param e BizException
     * @return ResponseEntity
     */
    @ExceptionHandler(BizException.class)
    public ResponseEntity<?> bizException(BizException e) {
        return commonReturn(Result.fail(e.getCode(), e.getMessage()));
    }

    @ExceptionHandler(ForbiddenException.class)
    public ResponseEntity<?> forbiddenException(ForbiddenException e) {
        return new ResponseEntity<>(HttpStatus.FORBIDDEN);
    }

    @ExceptionHandler(UnauthorizedException.class)
    public ResponseEntity<?> unauthorizedException(UnauthorizedException e) {
        return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
    }

    @ExceptionHandler(BadCredentialsException.class)
    public ResponseEntity<?> badCredentialsException(BadCredentialsException e) {
        return commonReturn(BizStatus.LOGIN_ERROR, e.getMessage());
    }

    @ExceptionHandler(UsernameNotFoundException.class)
    public ResponseEntity<?> usernameNotFoundException(UsernameNotFoundException e) {
        return commonReturn(BizStatus.LOGIN_ERROR, e.getMessage());
    }

    @ExceptionHandler(DisabledException.class)
    public ResponseEntity<?> DisabledException(DisabledException e) {
        return commonReturn(BizStatus.USER_DISABLED, e.getMessage());
    }


    public static final String SIGN = ":";

    /**
     * 参数异常
     *
     * @param e e
     * @return {@link ResponseEntity}<{@link ?}>
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<?> illegalArgumentException(IllegalArgumentException e) {
        String message = e.getMessage();
        // 一般情况下包含:不是人工抛的
        if (message.contains(SIGN)) {
            log.error("IllegalArgumentException异常", e);
        }
        return commonReturn(BizStatus.ILLEGAL_ARGUMENT, message);
    }

    /**
     * SpringMVC 请求方法不正确
     * 例如说，A 接口的方法为 GET 方式，结果请求方法为 POST 方式，导致不匹配
     *
     * @param e HttpRequestMethodNotSupportedException
     * @return ResponseEntity
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<?> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return commonReturn(BizStatus.REQUEST_PARAMETER, String.format("请求方法不正确:%s", e.getMessage()));
    }

    /**
     * 注解@requestparam 请求参数缺失
     * 例如说，接口上设置了 @RequestParam("xx") 参数，结果并未传递 xx 参数
     *
     * @param e MissingServletRequestParameterException
     * @return ResponseEntity
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseEntity<?> missingServletRequestParameterException(MissingServletRequestParameterException e) {
        return commonReturn(BizStatus.REQUEST_PARAMETER, String.format("请求参数缺失:%s", e.getParameterName()));
    }

    /**
     * 注解@requestparam 请求参参数类型错误
     * 例如说，接口上设置了 @RequestParam("xx") 参数为 Integer，结果传递 xx 参数类型为 String
     *
     * @param e MethodArgumentTypeMismatchException
     * @return ResponseEntity
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<?> methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        return commonReturn(BizStatus.REQUEST_PARAMETER, String.format("请求参数类型错误:%s", e.getName()));
    }

    /**
     * SpringMVC 参数绑定不正确，本质上也是通过 Validator 校验
     *
     * @param e BindException
     * @return ResponseEntity
     */
    @ExceptionHandler(BindException.class)
    public ResponseEntity<?> bindException(BindException e) {
        FieldError fieldError = e.getFieldError();
        return commonReturn(BizStatus.REQUEST_PARAMETER, String.format("请求参数错误:%s", Objects.isNull(fieldError) ? "未知" :
                Objects.equals(fieldError.getDefaultMessage(), "不能为空") ? fieldError.getField() + fieldError.getDefaultMessage() : fieldError.getDefaultMessage()));
    }

    /**
     * validate 异常
     * -参数校验不正确
     *
     * @param e MethodArgumentNotValidException
     * @return ResponseEntity
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> methodArgumentNotValidException(MethodArgumentNotValidException e) {
        FieldError fieldError = e.getBindingResult().getFieldError();
        if (fieldError == null) {
            return commonReturn(BizStatus.REQUEST_PARAMETER, e.getMessage());
        }
        String defaultMessage = fieldError.getDefaultMessage();
        defaultMessage = defaultMessage != null && (defaultMessage.startsWith("不能") || defaultMessage.startsWith("个数")) ? fieldError.getField() + defaultMessage : defaultMessage;
        return commonReturn(BizStatus.REQUEST_PARAMETER, String.format("请求参数不正确:%s", defaultMessage));
    }

    /**
     * validate 异常
     * -@Validated 写在class 上的 单个注解 @NotBlank(message = "唯一标识不为空")
     *
     * @param e ConstraintViolationException
     * @return ResponseEntity
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<?> constraintViolationException(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        // 多个异常信息
        return commonReturn(BizStatus.REQUEST_PARAMETER, violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining()));
    }

    /**
     * 其他一切 未知异常
     *
     * @param e Exception
     * @return ResponseEntity
     */
    @ExceptionHandler
    public ResponseEntity<?> defaultExceptionHandler(Exception e, HttpServletRequest request) {
        String uri = Objects.nonNull(request) ? request.getRequestURI() : "";
        log.error("未知异常uri={}，params={}，", uri, this.getParams(request), e);
        return commonReturn(BizStatus.UNKNOWN);
    }

    private ResponseEntity<?> commonReturn(BizStatus status) {
        return new ResponseEntity<>(Result.fail(status, status.getMessage()), HttpStatus.OK);
    }

    private ResponseEntity<?> commonReturn(BizStatus status, String msg) {
        return new ResponseEntity<>(Result.fail(status, msg), HttpStatus.OK);
    }

    private ResponseEntity<?> commonReturn(Result<Object> result) {
        return new ResponseEntity<>(result, HttpStatus.OK);
    }

    public static final String IGNORE_KEY = "_t";

    /**
     * 获取参数个数
     * 获取请求参数
     *
     * @param request HttpServletRequest
     * @return Map<String, Object>
     */
    protected String getParams(HttpServletRequest request) {
        if (Objects.isNull(request)) {
            return "";
        }
        String method = request.getMethod();
        if (RequestMethod.GET.name().equals(method)) {
            Map<String, String[]> parameters = request.getParameterMap();
            if (parameters.isEmpty()) {
                return "";
            }
            Map<String, Object> result = new HashMap<>(16);
            parameters.forEach((key, value) -> {
                if (value.length == 1) {
                    if (!IGNORE_KEY.equals(key)) {
                        result.put(key, value[0]);
                    }
                } else {
                    result.put(key, value);
                }
            });
            return this.subLength(result.toString());
        }
        if (RequestMethod.POST.name().equals(method) || RequestMethod.PUT.name().equals(method)
                || RequestMethod.PATCH.name().equals(method) || RequestMethod.DELETE.name().equals(method)) {
            return method;
        }
        return "";
    }

    public static final int MAX_LENGTH = 500;

    /**
     * 截取长度
     *
     * @param s 年代
     * @return {@link String}
     */
    private String subLength(String s) {
        return s.length() > MAX_LENGTH ? s.substring(0, MAX_LENGTH) + "..." : s;
    }
}
